#pragma once

using namespace System::Collections::Generic;
using namespace System::Collections;
using namespace System;

#include <Settings.h>

#pragma unmanaged
#include <boost/shared_ptr.hpp>
#include <ql/handle.hpp>
#include <vector>
#pragma managed

namespace Cephei
{

    public enum class NativeFeature
    {
          Handle
        , shared_ptr
        , Value
    };

	generic <typename T>
	public interface class INativeVector
	{
	public: 
		property int Count {int get ();}
		property T default[int] {T get (int index);void set (int index,T value);}
		/// <summary>
		/// Ensure that the Native has this Feature type.  If it does not, then prepare it
		/// </summary>
	public:
		bool PrepareFeature (NativeFeature feature);
	};

	generic <typename MT>
	public ref class CoVector : public  IVector<MT>
	{
	protected:
		List<MT>^ _list;
		INativeVector<MT>^ _nativeVector;
		CellChange^ _Change;				// multicast delegate
		IModel^ _Parent;
		String^ _Mnmonic;
		ITransaction^ _transaction;
        boost::detail::spinlock* _pSpinlock;

	protected:
		/////////////////////////////////////////////////////////////////////////////////
		// Constructors
		void init ()
		{
			_pSpinlock = new boost::detail::spinlock ();
		}

	internal:
		CoVector (INativeVector<MT>^ native) 
		{
			_list = gcnew List<MT> ();
			_nativeVector = native;
			for (int c = 0; c < native->Count; c++)
			{
				_list->Add (native[c]);
			}
			init ();
		}
		
	internal:
		CoVector (System::Collections::Generic::IEnumerable<MT>^ source, INativeVector<MT>^ native) 
		{
			_list = gcnew List<MT> (source);
			_nativeVector = native;
			init ();
		}
		~CoVector () 
		{
			if (_pSpinlock != NULL)
				delete _pSpinlock;
			_pSpinlock = NULL;
		}
			
		!CoVector () 
		{
			if (_pSpinlock != NULL)
				delete _pSpinlock;
		}
			
		property INativeVector<MT>^ Native
		{
			INativeVector<MT>^ get ()
			{
				return _nativeVector;

			}
		}

		/////////////////////////////////////////////////////////////////////////////////
		// IVector<MT> Methods

	public:
		INativeVector<MT>^ getFeature (NativeFeature feature)
		{
	    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
			if (_nativeVector->PrepareFeature (feature))
			{
				int c  = 0;
				for each (MT t in _list)
				{
					_nativeVector[c++] = t;
				}
			}
			return _nativeVector;
		}

		/////////////////////////////////////////////////////////////////////////////////
		// IList<MT> Methods
	public:
		virtual int IndexOf(MT item)
		{
			return _list->IndexOf (item);

		}

		// Insert is not implemented because it is just not very functional
		virtual void Insert(int index, MT item)
		{
			throw gcnew ListMethodNotImplementedException ();
		}

		// RemoveAt is not implemented because it does not map to a functional model
		virtual void RemoveAt(int index)
		{
			throw gcnew ListMethodNotImplementedException ();
		}

	public: 
		property MT default[int] //Item(int index)
		{
			virtual MT get (int index)
			{
				return _list[index];
			}
			virtual void set (int index, MT value)
			{
		    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
				_list[index] = value;
				_nativeVector[index] = value;
				if (_transaction != nullptr)
					OnChange (Cephei::CellEventType::Change, this, DateTime::Now, _transaction);
			}
		}

		/////////////////////////////////////////////////////////////////////////////
	    // ICollection<MT> Methods
		virtual void Add(MT item)
		{
	    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
			_list->Add (item);
			_nativeVector[_list->Count] = item;
            if (_transaction != nullptr)
				OnChange (Cephei::CellEventType::Change, this, DateTime::Now, _transaction);
		}
		virtual void Clear()
		{
	    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
			_list->Clear ();
			_nativeVector = nullptr;
		}
		virtual bool Contains(MT item)
		{
			return _list->Contains (item);
		}
		virtual void CopyTo(array<MT>^ a, int arrayIndex)
		{
			_list->CopyTo (a, arrayIndex);
		}
		
		// not a very functional function
		virtual bool Remove(MT item)
		{
			throw gcnew ListMethodNotImplementedException ();
		}
		property int Count 
		{ 
			virtual int get ()
			{
				return _list->Count;
			}
		}
		
		property bool IsReadOnly 
		{ 
			virtual bool get ()
			{
				return false;
			}
		}
		property bool HasListeners
        {
            virtual bool get ()
            {
                return (_Change != nullptr);
            }
        }

		property Cephei::ITransaction^ Transaction 
        { 
            virtual Cephei::ITransaction^ get ()
            {
               return _transaction; 
            }
            virtual void set (Cephei::ITransaction^ value)
            {
                _transaction = value;
            }
        }
		
		virtual void Copy(Cephei::ICell^ other)
        {
            IVector<MT>^ tother = dynamic_cast<IVector<MT>^> (other);
            if (tother != nullptr)
            {
				for each (MT t in tother)
				{
					Add (t);
				}
			}
        }



		///////////////////////////////////////////////////////////////////////////
		// IEnumerable<MT>
	public:
		virtual System::Collections::IEnumerator^ GetEnumeratorB() = System::Collections::IEnumerable::GetEnumerator
		{
			return _list->GetEnumerator();
		}

	public: 
		virtual System::Collections::Generic::IEnumerator<MT>^ GetEnumerator() = System::Collections::Generic::IEnumerable<MT>::GetEnumerator
		{
			return _list->GetEnumerator();
		}

		///////////////////////////////////////////////////////////////////////////
		// ICell
	public: 
		virtual void Lock ()
		{
			if (Cephei::QL::Settings::_ReferenceLocking)
    			_pSpinlock->lock ();
		}
		virtual void Unlock ()
		{
			_pSpinlock->unlock ();
		}

	public:
		event CellChange^ Change
		{
			virtual void add (CellChange^ p)
			{
				_Change += p;
			}
			virtual void remove (CellChange^ p)
			{
				_Change -= p;
			}
			virtual void raise (CellEventType eventType, ICell^ sender, DateTime epoch, ITransaction^ transaction)
			{
				if (_Change != nullptr)
				{
					_Change (eventType, sender, epoch, transaction);
				}
			}
		}
	public:
		virtual void OnChange (CellEventType eventType, ICell^ sender, DateTime thisEpoch, ITransaction^ transaction)
		{
			Change (eventType, sender, thisEpoch, transaction);
		}

	public:
		virtual property IModel^ Parent 
		{
			IModel^ get ()
			{
				return _Parent;
			}
			void set (IModel^ value)
			{
				_Parent = value;
			}
		}
		
	public:
		virtual property String^ Mnmonic
		{
			String^ get ()
			{
				return _Mnmonic;
			}
			void set (String^ mnmonic)
			{
				_Mnmonic = mnmonic;
			}
		}

		virtual property CellMethod Method 
		{ 
			virtual CellMethod get ()
			{
				return CellMethod::Unknown;
			}
		}
		virtual property DateTime Epoch
		{
			virtual DateTime get ()
			{
				return DateTime::Now;
			}
		}
		virtual property Object^ BoxValue
		{
			virtual Object^ get ()
			{
				return this;
			}
		}
		virtual void TransferTo (ICell^ cell)
		{
            if ( _Parent != nullptr)
            {
                _Parent->Remove (_Mnmonic);
                ((System::Collections::Generic::IDictionary<String^, ICell^>^)_Parent)->Add (_Mnmonic, cell);
            }
            if (_Change != nullptr)
            {
                for each (CellChange^ c in _Change->GetInvocationList ())
                {
                    cell->Change += safe_cast<CellChange^> (c->Clone ());
                }
                Change (CellEventType::Link, this, DateTime::Now, nullptr);
                for each (CellChange^ c in _Change->GetInvocationList ())
                {
                    Change -= c;
                }
            }
		}
	};

	generic <class MT>  where MT : ICell 
	public ref class CoCellVector : public  CoVector<MT>
	{
	internal:
		CoCellVector (INativeVector<MT>^ native) : CoVector<MT> (native) {}
	internal:
		CoCellVector (System::Collections::Generic::IEnumerable<MT>^ source, INativeVector<MT>^ native)  : CoVector<MT> (source, native) {}

	public: 
		property MT default[int] 
		{
			virtual MT get (int index) override 
			{
				return _list[index];
			}
			virtual void set (int index, MT value) override 
			{
		    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
				_list[index] = value;
				_nativeVector[index] = value;
				value->Change += gcnew CellChange (this, &CoVector<MT>::OnChange);
				if (_transaction != nullptr)
					OnChange (Cephei::CellEventType::Change, this, DateTime::Now, _transaction);
			}
		}
		virtual void Add(MT item) override 
		{
	    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
			_list->Add (item);
			_nativeVector[_list->Count] = item;
			item->Change += gcnew CellChange (this, &CoVector<MT>::OnChange);
			if (_transaction != nullptr)
				OnChange (Cephei::CellEventType::Change, this, DateTime::Now, _transaction);
		}
		virtual void Clear() override
		{
	    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
			for each (MT t in _list)
			{
				t->Change -= gcnew CellChange (this, &CoVector<MT>::OnChange);
			}
			_list->Clear ();
			_nativeVector = nullptr;
		}
	};
}